package model

import (
	"mini-sql/utils"
	"reflect"
)

type Expr struct {
	ExprType   ExprType `json:"expr_type,omitempty"`
	Children   []*Expr  `json:"children,omitempty"`
	IntValue   int64    `json:"int_value,omitempty"`
	FloatValue float64  `json:"float_value,omitempty"`
	StrValue   string   `json:"str_value,omitempty"`
}

var (
	NullExpr = Expr{
		ExprType: ExprTypeNull,
	}

	TrueExpr = Expr{
		ExprType: ExprTypeBool,
		IntValue: 1,
	}

	FalseExpr = Expr{
		ExprType: ExprTypeBool,
		IntValue: 0,
	}
)

func (e *Expr) IsValue() bool {
	return e.ExprType >= MinValueType && e.ExprType <= MaxValueType
}

func (e *Expr) ToBool() bool {
	if e == nil {
		return false
	}
	switch e.ExprType {
	case ExprTypeBool:
		return e.IntValue == 1
	case ExprTypeStr:
		return e.StrValue != ""
	case ExprTypeInt64:
		return e.IntValue != 0
	}
	return false
}

func (e *Expr) GetChild(index int) *Expr {
	if index >= len(e.Children) {
		return nil
	}
	return e.Children[index]
}

func (e *Expr) String() string {
	return utils.FormatJSON(e)
}

func (e *Expr) ToValue() interface{} {
	switch e.ExprType {
	case ExprTypeInt64:
		return e.IntValue
	case ExprTypeFloat:
		return e.FloatValue
	case ExprTypeStr:
		return e.StrValue
	case ExprTypeBool:
		return e.IntValue == 1
	}
	utils.Panicf("unsupported type: %v", e)
	return nil
}

func NewExprFromValue(value interface{}) *Expr {
	switch typed := value.(type) {
	case int64:
		return NewExprFromInt(typed)
	case string:
		return NewExprFromStr(typed)
	case float64:
		return NewExprFromFloat(typed)
	case bool:
		return NewExprFromBool(typed)
	}
	utils.Panicf("unsupported type: %v", reflect.TypeOf(value))
	return nil
}

func NewNullExpr() *Expr {
	return &NullExpr
}

func NewExprFromInt(value int64) *Expr {
	return &Expr{
		ExprType: ExprTypeInt64,
		IntValue: value,
	}
}

func NewExprFromStr(value string) *Expr {
	return &Expr{
		ExprType: ExprTypeStr,
		StrValue: value,
	}
}

func NewExprFromFloat(value float64) *Expr {
	return &Expr{
		ExprType:   ExprTypeFloat,
		FloatValue: value,
	}
}

func NewExprFromBool(value bool) *Expr {
	if value {
		return NewTrueExpr()
	} else {
		return NewFalseExpr()
	}
}

func NewTrueExpr() *Expr {
	return &TrueExpr
}

func NewFalseExpr() *Expr {
	return &FalseExpr
}
